#include "boost/tuple/tuple.hpp"
#include <iostream>
#include <algorithm>
#include <vector>
#include <fstream>
#include <ext/hash_map>
#include <istream>
#include <map>
#include <deque>
#include <cassert>

using std::istream;
using std::vector;
using std::string;
using std::cin;
using std::cout;
using std::cerr;
using std::map;
using std::deque;
//using std::assert;

struct eqstr
{
	bool operator()(const char* s1, const char* s2) const
	{	return strcmp(s1, s2) == 0;	}
};



typedef __gnu_cxx::hash_map<const char*, unsigned,
							__gnu_cxx::hash<const char*>, eqstr> t_map_str_uint;

bool lookup_gene_index(		const string&		name,
							t_map_str_uint&		gene_name_to_gi,
							deque<string>&		gi_to_gene_name,
							unsigned&			gene_index)
{
	// if name is already there
	t_map_str_uint::iterator p_index = gene_name_to_gi.find(name.c_str());
	if (p_index != gene_name_to_gi.end())
	{
		gene_index = p_index->second;
		return false;
	}

	// name is new
	gene_index = gi_to_gene_name.size();
	gi_to_gene_name.push_back(name);
	gene_name_to_gi[gi_to_gene_name.back().c_str()] = gene_index;
	return true;
}

void matrix_from_pairs	(istream& 					is,
					   	deque<string>&				gi_to_gene_name,
						deque<deque<unsigned> >&	gi_to_gi_links,
						deque<deque<double> >&  	gi_to_dist)
{
	t_map_str_uint		gene_name_to_gi;
	gene_name_to_gi.resize(500000);
	
	string gene_name1, gene_name2;
	double dist;
	is >> gene_name1 >> gene_name2 >> dist;
	unsigned line_cnt = 0;
	while (is)
	{
		if ((++line_cnt % 15000) == 0)
		    cerr << ".";
		unsigned gi1, gi2;
		if (lookup_gene_index(	gene_name1, gene_name_to_gi, gi_to_gene_name, gi1))
		{
			gi_to_dist.push_back(deque<double>());
			gi_to_gi_links.push_back(deque<unsigned>());
		}
		if (lookup_gene_index(	gene_name2, gene_name_to_gi, gi_to_gene_name, gi2))
		{
			gi_to_dist.push_back(deque<double>());
			gi_to_gi_links.push_back(deque<unsigned>());
		}
		if (gi1 == gi2)
		{
			is >> gene_name1 >> gene_name2 >> dist;
			continue;
		}

		gi_to_gi_links[gi1].push_back(gi2);
		gi_to_dist[gi1].push_back(dist);
		gi_to_gi_links[gi2].push_back(gi1);
		gi_to_dist[gi2].push_back(dist);

		is >> gene_name1 >> gene_name2 >> dist;
	}
}

int main(int argc, char* argv[])
{
	if (argc < 2)
	{
		cerr << "First argument should be mapping file name (index to names)\n";
		return 1;
	}

	std::ofstream ofs(argv[1]);
	if (!ofs)
	{
		cerr	<< "First argument should be mapping file name (index to names)\n"
				<< "Could not open " << argv[1] << "\n";
		return 1;
	}

	deque<string>			gi_to_gene_name;
	deque<deque<unsigned> >	gi_to_gi_links;
	deque<deque<double> >  	gi_to_dist;
	matrix_from_pairs(cin, gi_to_gene_name, gi_to_gi_links, gi_to_dist);

	unsigned matrix_size = gi_to_gi_links.size();	
	cout	<< "(mclheader\nmcltype matrix\ndimensions "
			<< matrix_size << 'x' << matrix_size << "\n)\n";
	cout 	<< "(mcldoms\n";
	for (unsigned i = 0; i < gi_to_gi_links.size(); ++i)
		cout << i << " ";
	cout << "$\n)\n";
	cout << "(mclmatrix\nbegin\n";

	for (unsigned i = 0; i < gi_to_gi_links.size(); ++i)
	{
		cout	<< i << " ";
		for (unsigned j = 0; j < gi_to_gi_links[i].size(); ++j)
			cout	<< gi_to_gi_links[i][j] << ":"
					<< gi_to_dist[i][j]  << " ";
		cout << "$" << std::endl;
	}
	cout << ")\n";
	
	for (unsigned i = 0; i < gi_to_gi_links.size(); ++i)
		ofs << i << "\t" << gi_to_gene_name[i] << "\n";

	return 0;
}
